1 module targets.psvita; 2 import std.exception; 3 import commons; 4 5 private enum updateCmd = "sudo apt-get update"; 6 private enum depsInstallCmd = "sudo apt-get install make git-core cmake python curl wget"; 7 private enum vdpmRepo = "https://github.com/vitasdk/vdpm"; 8 private enum bootstrapVsdk = "./bootstrap-vitasdk.sh"; 9 private enum installAllVsdk = "./install-all.sh"; 10 private enum vitasdkExports = 11 "\n"~"export VITASDK=/usr/local/vitasdk" ~ 12 "\n"~"export PATH=$VITASDK/bin:$PATH #adds vitasdk tool to $PATH"; 13 14 private enum vdpmInstallCmd = 15 "git clone https://github.com/vitasdk/vdpm && "~ 16 "cd vdpm && "~ 17 "./bootstrap-vitasdk-.sh "~ 18 "./install-all.sh"; 19 20 21 private bool setupPsvitaLinux(ref Terminal t, ref RealTimeConsoleInput input) 22 { 23 std.file.mkdirRecurse("/usr/local/vitasdk"); 24 std.file.append(buildPath(environment["HOME"], ".bashrc"), vitasdkExports); 25 wait(spawnShell(updateCmd)); 26 wait(spawnShell(depsInstallCmd)); 27 wait(spawnShell(vdpmInstallCmd)); 28 wait(spawnShell("vitasdk-update")); 29 return true; 30 } 31 private string getWslSource() 32 { 33 return executeShell("wsl echo -n $(wslpath \"%USERPROFILE%\")/.bashrc").output; 34 } 35 36 private auto getExecFunc() 37 { 38 return (scope string arg) 39 { 40 version(Windows){return wait(spawnShell("wsl source "~getWslSource~" ^&^& "~arg));} 41 else return wait(spawnShell(arg)); 42 }; 43 } 44 45 /** 46 * The first build will simply generate the VPK, by mapping assets, compiling 47 * all the required stuff for PSVita, and also including the main binary (eboot.bin) 48 * after the first build is done, one must manually install the vita_sample.vpk 49 * by using the vitashell utility. 50 * Params: 51 * t = 52 * Returns: 53 */ 54 private bool firstBuild(ref Terminal t) 55 { 56 string cwd = std.file.getcwd(); 57 std.file.chdir(getHipPath("build", "vita", "hipreme_engine")); 58 scope(exit) std.file.chdir(cwd); 59 auto exec = getExecFunc(); 60 if(exec("make") != 0) 61 { 62 t.writelnError("Make failed."); 63 return false; 64 } 65 if(exec("curl ftp://"~configs["psvIp"].str~":1337/ux0:/ -T ./vita_sample.vpk") != 0) 66 { 67 t.writelnError("Could not send the VPK."); 68 return false; 69 } 70 return true; 71 } 72 73 /** 74 * Instead of building the entire VPK for PS Vita, it only changes the binary, after that, 75 * it directly sends this new binary to the Package folder, so, there will be no need 76 * to extract a VPK by going into the Install Process again, making it a lot faster 77 * to both build and test. 78 * 79 * For even faster installation, it is recomended to run a background FTP on PSV 80 * Params: 81 * t = 82 * Returns: 83 */ 84 private bool fastBuild(ref Terminal t) 85 { 86 enum APP_ID = "VSDK00007"; 87 auto exec = getExecFunc(); 88 89 string cwd = std.file.getcwd(); 90 std.file.chdir(getHipPath("build", "vita", "hipreme_engine")); 91 92 version(Windows) enum pipe = "^|"; 93 else enum pipe = "|"; 94 95 scope(exit) std.file.chdir(cwd); 96 exec("make clean"); 97 if(exec("make eboot.bin") != 0) 98 { 99 t.writelnError("Could not rebuild."); 100 return false; 101 } 102 exec("echo screen on "~pipe~" nc "~configs["psvIp"].str~" "~configs["psvCmdPort"].str); 103 if(exec("curl ftp://"~configs["psvIp"].str~":1337/ux0:/app/"~APP_ID~"/ -T ./eboot.bin") != 0) 104 { 105 t.writelnError("Could not send eboot.bin"); 106 return false; 107 } 108 exec("echo launch "~APP_ID~" "~pipe~" nc "~configs["psvIp"].str~" "~configs["psvCmdPort"].str); 109 return true; 110 } 111 112 private bool setupPsvitaWindows(ref Terminal t, ref RealTimeConsoleInput input) 113 { 114 if(executeShell("where wsl").status != 0) 115 { 116 t.writelnError("Please, run a command prompt with administrator access and run `wsl --install` before developing for PSV on Windows."); 117 return false; 118 } 119 string cwd = std.file.getcwd(); 120 std.file.mkdirRecurse(buildPath(cwd, "PSVita", "vitasdk")); 121 122 string bashRc = buildPath(environment["USERPROFILE"], ".bashrc"); 123 string fileToSource = getWslSource(); 124 if(std.file.exists(bashRc)) 125 { 126 if(executeShell("wsl source"~fileToSource~" ^&^& export -p ^| grep VITASDK").status != 0) 127 std.file.append(bashRc, vitasdkExports); 128 } 129 else std.file.append(bashRc, vitasdkExports); 130 131 auto wslExec = (scope string[] commands...) 132 { 133 import std.array:join; 134 t.writelnHighlighted("WSL Execution: "~commands); 135 return wait(spawnShell("wsl source "~fileToSource~" ^&^& "~join(commands, " "))); 136 }; 137 138 if(wslExec(updateCmd) != 0) 139 { 140 t.writelnError("Could not update system repositores"); 141 return false; 142 } 143 if(wslExec(depsInstallCmd) != 0) 144 { 145 t.writelnError("Could not setup vita dependencies"); 146 return false; 147 } 148 std.file.chdir(buildPath(cwd, "PSVita")); 149 if(!std.file.exists("vdpm")) 150 { 151 if(wslExec("git clone "~vdpmRepo) != 0) 152 { 153 t.writelnError("Could not clone vdpm"); 154 return false; 155 } 156 } 157 std.file.chdir(buildPath(cwd, "PSVita", "vdpm")); 158 if(wslExec("which vitasdk-update") != 0 && wslExec(bootstrapVsdk) != 0) 159 { 160 t.writelnError("Could not execute "~bootstrapVsdk); 161 wslExec("sudo rm -rf /usr/local/vitasdk"); 162 return false; 163 } 164 if(wslExec(installAllVsdk) != 0) 165 { 166 t.writelnError("Could not execute "~installAllVsdk); 167 return false; 168 } 169 std.file.chdir(cwd); 170 if(wslExec("vitasdk-update") != 0) 171 { 172 t.writelnError("Could not update vitasdk"); 173 return false; 174 } 175 configs["firstPsvConfig"] = true; 176 updateConfigFile(); 177 return true; 178 } 179 180 bool setupPsvita(ref Terminal t, ref RealTimeConsoleInput input) 181 { 182 if(!extractToFolder( 183 getHipPath("build", "vita", "hipreme_engine", "hipreme_engine_vita_dev_files.7z"), 184 getHipPath("build", "vita", "hipreme_engine"), 185 t, input 186 )) 187 { 188 t.writelnError("PSVita requires 7zip to extract the development files."); 189 return false; 190 } 191 //https://vitasdk.org/ 192 version(Windows) return setupPsvitaWindows(t, input); 193 else version(linux) return setupPsvitaLinux(t, input); 194 else assert(false, "Not supported"); 195 } 196 197 ChoiceResult preparePSVita(Choice* c, ref Terminal t, ref RealTimeConsoleInput input, in CompilationOptions cOpts) 198 { 199 if(!("firstPsvConfig" in configs) || !configs["firstPsvConfig"].boolean) 200 { 201 if(!setupPsvita(t, input)) 202 return ChoiceResult.Error; 203 } 204 cached(() => timed(() => loadSubmodules(t, input))); 205 cached(() => timed(() => outputTemplateForTarget(t))); 206 runEngineDScript(t, "releasegame.d", configs["gamePath"].str); 207 putResourcesIn(t, getHipPath("build", "vita", "hipreme_engine", "assets")); 208 209 string dflags = "-I="~configs["hipremeEnginePath"].str~"/modules/d_std/source "~ 210 "-I="~configs["hipremeEnginePath"].str~"/dependencies/runtime/druntime/arsd-webassembly "~ 211 "-d-version=PSVita " ~ 212 "-d-version=PSV " ~ 213 "-mtriple=armv7a-unknown-unknown " ~ 214 "--revert=dtorfields "~ 215 "-mcpu=cortex-a9 "~ 216 "-O0 " ~ 217 "-g "~ 218 "-float-abi=hard "~ 219 "--relocation-model=static "~ 220 "-d-version=CarelessAlocation "~ 221 "-d-version=ArsdUseCustomRuntime "; 222 223 environment["DFLAGS"] = dflags; 224 225 226 requireConfiguration("psvIp", "Set up PSVita IP for installing your application via FTP.", t, input); 227 requireConfiguration("psvCmdPort", "Set up PSVita Command Port for automatic execution after compilation+installation.", t, input); 228 229 230 std.file.chdir(configs["hipremeEnginePath"].str); 231 232 if(waitDubTarget(t, "psvita", DubArguments("build --parallel --deep --compiler=ldc2 --arch=armv7a-unknown-unknown ")) != 0) 233 { 234 t.writelnError("Could not build for PSVita."); 235 return ChoiceResult.Error; 236 } 237 environment["DFLAGS"] = ""; 238 runEngineDScript(t, "copylinkerfiles.d", 239 "\"--compiler=ldc2 --arch=armv7a-unknown-unknown " ~ 240 "--recipe="~buildPath(getBuildTarget("psvita"), "dub.json")~'"', 241 getHipPath("build", "vita", "hipreme_engine", "libs"), 242 dflags); 243 244 static bool isFirstBuild = true; 245 if(isFirstBuild) 246 { 247 if(!firstBuild(t)) 248 { 249 t.writelnError("Could not build PSVita vita_sample.vpk"); 250 return ChoiceResult.Error; 251 } 252 isFirstBuild = false; 253 } 254 else 255 { 256 if(!fastBuild(t)) 257 { 258 t.writelnError("Could not do subsequent builds."); 259 return ChoiceResult.Error; 260 } 261 } 262 263 return ChoiceResult.None; 264 }